Skip to contentMethod: defineBean(String, Object)
1: /*
2: * Copyright (c) 2012, 2021 Oracle and/or its affiliates and others.
3: * All rights reserved.
4: *
5: * This program and the accompanying materials are made available under the
6: * terms of the Eclipse Public License v. 2.0, which is available at
7: * http://www.eclipse.org/legal/epl-2.0.
8: *
9: * This Source Code may also be made available under the following Secondary
10: * Licenses when the conditions for such availability set forth in the
11: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
12: * version 2 with the GNU Classpath Exception, which is available at
13: * https://www.gnu.org/software/classpath/license.html.
14: *
15: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
16: */
17:
18: package jakarta.el;
19:
20: import java.lang.reflect.Array;
21: import java.lang.reflect.Method;
22: import java.lang.reflect.Modifier;
23:
24: /**
25: * Provides an API for using Jakarta Expression Language in a stand-alone environment.
26: *
27: * <p>
28: * This class provides a direct and simple interface for
29: * <ul>
30: * <li>Evaluating Jakarta Expression Language expressions.</li>
31: * <li>Assigning values to beans or setting a bean property.</li>
32: * <li>Setting a {@link ValueExpression} to a Jakarta Expression Language variable.</li>
33: * <li>Defining a static method as Jakarta Expression Language function.</li>
34: * <li>Defining an object instance as Jakarta Expression Language name.
35: * </ul>
36: *
37: * <p>
38: * This API is not a replacement for the APIs in Jakarta Expression Language 2.2. Containers that maintain Jakarta
39: * Expression Language environments can continue to do so, without using this API.
40: *
41: * <p>
42: * For Jakarta Expression Language users who want to manipulate Jakarta Expression Language environments, like adding
43: * custom {@link ELResolver}s, {@link ELManager} can be used.
44: *
45: * <h3>Scope and Life Cycle</h3>
46: * <p>
47: * Since it maintains the state of the Jakarta Expression Language environments, <code>ELProcessor</code> is not thread
48: * safe. In the simplest case, an instance can be created and destroyed before and after evaluating Jakarta Expression
49: * Language expressions. A more general usage is to use an instance of <code>ELProcessor</code> for a session, so that
50: * the user can configure the Jakarta Expression Language evaluation environment for that session.
51: * </p>
52: *
53: * <h3>Automatic Bracketing of Expressions</h3>
54: * <p>
55: * A note about the Jakarta Expression Language expressions strings used in the class. The strings allowed in the methods
56: * {@link ELProcessor#getValue}, {@link ELProcessor#setValue}, and {@link ELProcessor#setVariable} are limited to
57: * non-composite expressions, i.e. expressions of the form ${...} or #{...} only. Also, it is not necessary (in fact not
58: * allowed) to bracket the expression strings with ${ or #{ and } in these methods: they will be automatically
59: * bracketed. This reduces the visual cluster, without any lost of functionalities (thanks to the addition of the
60: * concatenation operator).
61: *
62: * <h3>Example</h3> The following code snippet illustrates the use of ELProcessor to define a bean and evaluate its
63: * property. <blockquote>
64: *
65: * <pre>
66: * ELProcessor elp = new ELProcessor();
67: * elp.defineBean("employee", new Employee("Charlie Brown"));
68: * String name = elp.eval("employee.name");
69: * </pre>
70: *
71: * </blockquote>
72: *
73: * @since Jakarta Expression Language 3.0
74: */
75: public class ELProcessor {
76:
77: private ELManager elManager = new ELManager();
78: private ExpressionFactory factory = ELManager.getExpressionFactory();
79:
80: /**
81: * Return the ELManager used for Jakarta Expression Language processing.
82: *
83: * @return The ELManager used for Jakarta Expression Language processing.
84: */
85: public ELManager getELManager() {
86: return elManager;
87: }
88:
89: /**
90: * Evaluates an Jakarta Expression Language expression.
91: *
92: * @param expression The Jakarta Expression Language expression to be evaluated.
93: * @return The result of the expression evaluation.
94: */
95: public <T> T eval(String expression) {
96: @SuppressWarnings("unchecked")
97: T result = (T) getValue(expression, Object.class);
98: return result;
99: }
100:
101: /**
102: * Evaluates an Jakarta Expression Language expression, and coerces the result to the specified type.
103: *
104: * @param expression The Jakarta Expression Language expression to be evaluated.
105: * @param expectedType Specifies the type that the resultant evaluation will be coerced to.
106: * @return The result of the expression evaluation.
107: */
108: public <T> T getValue(String expression, Class<T> expectedType) {
109: ValueExpression exp = factory.createValueExpression(elManager.getELContext(), bracket(expression), expectedType);
110: return exp.getValue(elManager.getELContext());
111: }
112:
113: /**
114: * Sets an expression with a new value. The target expression is evaluated, up to the last property resolution, and the
115: * resultant (base, property) pair is set to the provided value.
116: *
117: * @param expression The target expression
118: * @param value The new value to set.
119: *
120: * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property
121: * does not exist or is not readable.
122: * @throws PropertyNotWritableException if the final variable or property resolution failed because the specified
123: * variable or property is not writable.
124: * @throws ELException if an exception was thrown while attempting to set the property or variable. The thrown exception
125: * must be included as the cause property of this exception, if available.
126: */
127: public void setValue(String expression, Object value) {
128: ValueExpression exp = factory.createValueExpression(elManager.getELContext(), bracket(expression), Object.class);
129: exp.setValue(elManager.getELContext(), value);
130: }
131:
132: /**
133: * Assign a Jakarta Expression Language expression to a Jakarta Expression Language variable. The expression is parsed,
134: * but not evaluated, and the parsed expression is mapped to the Jakarta Expression Language variable in the local
135: * variable map. Any previously assigned expression to the same variable will be replaced. If the expression is
136: * <code>null</code>, the variable will be removed.
137: *
138: * @param var The name of the variable.
139: * @param expression The Jakarta Expression Language expression to be assigned to the variable.
140: */
141: public void setVariable(String var, String expression) {
142: ValueExpression exp = factory.createValueExpression(elManager.getELContext(), bracket(expression), Object.class);
143: elManager.setVariable(var, exp);
144: }
145:
146: /**
147: * Define a Jakarta Expression Language function in the local function mapper.
148: *
149: * @param prefix The namespace for the function or "" for no namesapce.
150: * @param function The name of the function. If empty (""), the method name is used as the function name.
151: * @param className The full Java class name that implements the function.
152: * @param method The name (specified without parenthesis) or the signature (as in the Java Language Spec) of the static
153: * method that implements the function. If the name (e.g. "sum") is given, the first declared method in class that
154: * matches the name is selected. If the signature (e.g. "int sum(int, int)" ) is given, then the declared method with
155: * the signature is selected.
156: *
157: * @throws NullPointerException if any of the arguments is null.
158: * @throws ClassNotFoundException if the specified class does not exists.
159: * @throws NoSuchMethodException if the method (with or without the signature) is not a declared method of the class, or
160: * if the method signature is not valid, or if the method is not a static method.
161: */
162: public void defineFunction(String prefix, String function, String className, String method) throws ClassNotFoundException, NoSuchMethodException {
163: if (prefix == null || function == null || className == null || method == null) {
164: throw new NullPointerException("Null argument for defineFunction");
165: }
166:
167: Method meth = null;
168: ClassLoader loader = Thread.currentThread().getContextClassLoader();
169: if (loader == null) {
170: loader = getClass().getClassLoader();
171: }
172:
173: Class<?> klass = Class.forName(className, false, loader);
174: int j = method.indexOf('(');
175: if (j < 0) {
176: // Just a name is given
177: for (Method m : klass.getDeclaredMethods()) {
178: if (m.getName().equals(method)) {
179: meth = m;
180: }
181: }
182: if (meth == null) {
183: throw new NoSuchMethodException("Bad method name: " + method);
184: }
185: } else {
186: // method is the signature
187: // First get the method name, ignore the return type
188: int p = method.indexOf(' ');
189: if (p < 0) {
190: throw new NoSuchMethodException("Bad method signature: " + method);
191: }
192:
193: String methodName = method.substring(p + 1, j).trim();
194:
195: // Extract parameter types
196: p = method.indexOf(')', j + 1);
197: if (p < 0) {
198: throw new NoSuchMethodException("Bad method signature: " + method);
199: }
200: String[] params = method.substring(j + 1, p).split(",");
201: Class<?>[] paramTypes = new Class<?>[params.length];
202: for (int i = 0; i < params.length; i++) {
203: paramTypes[i] = toClass(params[i], loader);
204: }
205: meth = klass.getDeclaredMethod(methodName, paramTypes);
206: }
207: if (!Modifier.isStatic(meth.getModifiers())) {
208: throw new NoSuchMethodException("The method specified in defineFunction must be static: " + meth);
209: }
210:
211: if (function.equals("")) {
212: function = method;
213: }
214:
215: elManager.mapFunction(prefix, function, meth);
216: }
217:
218: /**
219: * Define a Jakarta Expression Language function in the local function mapper.
220: *
221: * @param prefix The namespace for the function or "" for no namesapce.
222: * @param function The name of the function. If empty (""), the method name is used as the function name.
223: * @param method The <code>java.lang.reflect.Method</code> instance of the method that implements the function.
224: *
225: * @throws NullPointerException if any of the arguments is null.
226: * @throws NoSuchMethodException if the method is not a static method
227: */
228: public void defineFunction(String prefix, String function, Method method) throws NoSuchMethodException {
229: if (prefix == null || function == null || method == null) {
230: throw new NullPointerException("Null argument for defineFunction");
231: }
232: if (!Modifier.isStatic(method.getModifiers())) {
233: throw new NoSuchMethodException("The method specified in defineFunction must be static: " + method);
234: }
235: if (function.equals("")) {
236: function = method.getName();
237: }
238:
239: elManager.mapFunction(prefix, function, method);
240: }
241:
242: /**
243: * Define a bean in a local bean repository, hiding other beans of the same name.
244: *
245: * @param name The name of the bean
246: * @param bean The bean instance to be defined. If <code>null</code>, the name will be removed from the local bean
247: * repository.
248: */
249: public void defineBean(String name, Object bean) {
250: elManager.defineBean(name, bean);
251: }
252:
253: /**
254: * Return the Class object associated with the class or interface with the given name.
255: */
256: private static Class<?> toClass(String type, ClassLoader loader) throws ClassNotFoundException {
257: Class<?> c = null;
258: int i0 = type.indexOf('[');
259: int dims = 0;
260: if (i0 > 0) {
261: // This is an array. Count the dimensions
262: for (int i = 0; i < type.length(); i++) {
263: if (type.charAt(i) == '[') {
264: dims++;
265: }
266: }
267: type = type.substring(0, i0);
268: }
269:
270: if ("boolean".equals(type)) {
271: c = boolean.class;
272: } else if ("char".equals(type)) {
273: c = char.class;
274: } else if ("byte".equals(type)) {
275: c = byte.class;
276: } else if ("short".equals(type)) {
277: c = short.class;
278: } else if ("int".equals(type)) {
279: c = int.class;
280: } else if ("long".equals(type)) {
281: c = long.class;
282: } else if ("float".equals(type)) {
283: c = float.class;
284: } else if ("double".equals(type)) {
285: c = double.class;
286: } else {
287: c = loader.loadClass(type);
288: }
289:
290: if (dims == 0) {
291: return c;
292: }
293:
294: if (dims == 1) {
295: return Array.newInstance(c, 1).getClass();
296: }
297:
298: // Array of more than i dimension
299: return Array.newInstance(c, new int[dims]).getClass();
300: }
301:
302: private String bracket(String expression) {
303: return "${" + expression + '}';
304: }
305: }